package algebra.query.indexing;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import utils.exception.CoreReasoningException;
import utils.exception.OrderingDescriptionException;

import kb.DescriptionLogicsKB;
//import algebra.op.ConstantOP;
import algebra.op.AlgebraicOP;
import algebra.op.PrimaryOP;
import algebra.op.ProjectionOP;
//import algebra.op.ScanOP;
import algebra.op.SelectionOP;
import algebra.query.ObjectQuery;
import algebra.query.indexing.DescriptionIndex;
import algebra.query.od.OrderDep;
import algebra.query.od.OrderDepNoop;
import algebra.query.pd.ProjDesc;
//import algebra.visitors.ProjectionQueryVisitorEvaluator;
import algebra.visitors.AlgebraicOPEvaluator;
import algebra.visitors.cursors.CACursor;
import entities.assertion.ConceptAssertion;
import entities.concept.Concept;

//import entities.vocabulary.Display;

/**
 * An SI is a pair (C, Pd) with ordering Od
 * 
 * @author j55wu
 * 
 */
public class SecondaryIndex extends CachedIndex {

	// all indices -> file name with DI
	public static Map<SecondaryIndex, String> allCachedQueries = new HashMap<SecondaryIndex, String>();

	private final Concept indexingCondition;
	private final ProjDesc pd;
	private final DescriptionLogicsKB kb;
	private OrderDep od = new OrderDepNoop();
	private String name;
	private DescriptionIndex di;

	// dir location for exporting files

	public ProjDesc getPdi() {
		return this.pd;
	}

	public AlgebraicOP getPlanExpression() {
		SelectionOP sp = new SelectionOP(new PrimaryOP(kb), indexingCondition,
				kb);
		ProjectionOP jp = new ProjectionOP(pd, sp, kb);
		return jp;
	}

	public DescriptionLogicsKB getKB() {
		return this.kb;
	}

	public Concept getCi() {
		return this.indexingCondition;
	}

	public OrderDep getOdi() {
		return this.od;
	}

	/**
	 * 
	 * @param c
	 *            : the selection condition
	 * @param pd
	 *            : the projection description By default the ordering
	 *            description is to store by instance name, unless specified. <br>
	 *            Note that this Od is NOT the ordering description used in
	 *            evaluating the subquery c in (c,pd), rather it is the one used
	 *            in storing the retrieved assertions.
	 * @throws OrderingDescriptionException
	 */
	public SecondaryIndex(ObjectQuery q, DescriptionLogicsKB kb) {
		this(q.getC(), q.getPd(), null, kb);
	}

	public SecondaryIndex(ObjectQuery q, OrderDep mod, DescriptionLogicsKB kb) {
		this(q.getC(), q.getPd(), mod, kb);
	}

	public SecondaryIndex(Concept c, ProjDesc pd,
			DescriptionLogicsKB kb) {
		this(c, pd, null, kb);
	}

	public SecondaryIndex(Concept c, ProjDesc pd, OrderDep mod,
			DescriptionLogicsKB kb) {
		this.indexingCondition = c;
		this.pd = pd;
		if (mod != null) {
			this.od = mod;
		}
		this.kb = kb;
		this.name = Integer.toString(this.toSerializedString().hashCode());
		allCachedQueries.put(this, null);
	}

	public String getName() {
		return this.name;
	}

	// requires reasoning here to actually build the index
	public void buildIndex(String indexdir) {
		String indexlocation = indexdir;
		if(indexdir == null || indexdir.trim().equals("")){
			//use teh default location
			indexlocation = indexDir;
		}
		ObjectQuery iq = new ObjectQuery(this.indexingCondition, pd);
		List<ConceptAssertion> results = CACursor.getResultSet(iq
				.translateAsExpression(kb));
		try {
			boolean toBeBuilt = true;
			// if(allCachedQueries.containsKey(this) &&
			// allCachedQueries.get(this)!=null){
			// this.di =
			// this.readSerializedCacheIndex(allCachedQueries.get(this), null,
			// kb);
			// if(this.di==null){
			// toBeBuilt = true;
			// }
			// }else{
			// toBeBuilt = true;
			// }
			if (toBeBuilt) {
				this.di = AlgebraicOPEvaluator
						.indexingQueryResults(results, od);
				if (buildSave) {
					String sfn = name + extensionIndex;
					CachedIndex.serializeCacheIndex(this.toSerializedString(),
							this.di, null, indexlocation + sfn);
					allCachedQueries.put(this, sfn);
				}
			}
		} catch (CoreReasoningException e) {
			e.printStackTrace();
		}
		return;
	}

	@Override
	public String toString() {
		return this.kb.getKBName() + serializedNameSep + "["
				+ this.indexingCondition.toString() + "," + this.pd.toString()
				+ "]" + serializedNameSep
				+ (this.od == null ? "un" : this.od.toString());
	}

	public String toSerializedString() {
		return this.kb.getKBName() + CachedIndex.serializedNameSep
				+ this.indexingCondition.toSerializedString()
				+ CachedIndex.serializedNameSep + this.pd.toSerializedString()
				+ CachedIndex.serializedNameSep + this.od.toSerializedString();
	}

	public String shortName() {
		return "(" + this.indexingCondition.toString() + ","
				+ this.pd.toString() + ")::" + this.od.toString();
	}

	@Override
	public DescriptionIndex getDescriptionIndex() {
		return this.di;
	}

	public void setDI(DescriptionIndex di) {
		this.di = di;
	}

	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime
				* result
				+ ((indexingCondition == null) ? 0 : indexingCondition
						.hashCode());
		result = prime * result + ((kb == null) ? 0 : kb.hashCode());
		result = prime * result + ((od == null) ? 0 : od.hashCode());
		result = prime * result + ((pd == null) ? 0 : pd.hashCode());
		return result;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (!(obj instanceof SecondaryIndex))
			return false;
		SecondaryIndex other = (SecondaryIndex) obj;
		if (indexingCondition == null) {
			if (other.indexingCondition != null)
				return false;
		} else if (!indexingCondition.equals(other.indexingCondition))
			return false;
		if (kb == null) {
			if (other.kb != null)
				return false;
		} else if (!kb.equals(other.kb))
			return false;
		if (od == null) {
			if (other.od != null)
				return false;
		} else if (!od.equals(other.od))
			return false;
		if (pd == null) {
			if (other.pd != null)
				return false;
		} else if (!pd.equals(other.pd))
			return false;
		return true;
	}

}
